﻿using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Net.Mail;
using System.Web;
using System.Web.Security;

namespace PhoneStoreWeb.Models
{
    // Register and ChangePassword Models
    #region Models

    [PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "Hai mật khẩu không trùng khớp.")]
    public class ChangePasswordModel
    {
        [Required(ErrorMessage = "Chưa nhập \"Mật khẩu hiện tại\"")]
        [DataType(DataType.Password)]
        [DisplayName("Mật khẩu hiện tại")]
        public string OldPassword { get; set; }

        [Required(ErrorMessage = "Chưa nhập \"Mật khẩu mới\"")]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [DisplayName("Mật khẩu mới")]
        public string NewPassword { get; set; }

        [Required(ErrorMessage = "Chưa nhập \"Xác nhận mật khẩu\"")]
        [DataType(DataType.Password)]
        [DisplayName("Xác nhận mật khẩu")]
        public string ConfirmPassword { get; set; }
    }

    [PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "Hai mật khẩu không trùng khớp.")]
    public class ResetPasswordModel
    {
        [Required(ErrorMessage = "Chưa nhập \"Tên đăng nhập\"")]
        [DisplayName("Tên đăng nhập:")]
        public string UserName { get; set; }

        [Required(ErrorMessage = "Chưa nhập \"Mật khẩu mới\"")]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [DisplayName("Mật khẩu mới")]
        public string NewPassword { get; set; }

        [Required(ErrorMessage = "Chưa nhập \"Xác nhận mật khẩu\"")]
        [DataType(DataType.Password)]
        [DisplayName("Xác nhận mật khẩu")]
        public string ConfirmPassword { get; set; }
    }

    [PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "Hai mật khẩu không trùng khớp.")]
    public class RegisterModel
    {
        [Required(ErrorMessage = "Chưa nhập \"Tên đăng nhập\"")]
        [DisplayName("Tên đăng nhập:")]
        public string UserName { get; set; }

        [Required(ErrorMessage = "Chưa nhập \"Email\"")]
        [DataType(DataType.EmailAddress)]
        [RegularExpression(".+\\@.+\\..+", ErrorMessage = "Invalid Email.")]
        [DisplayName("Email:")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Chưa nhập \"Mật khẩu\"")]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [DisplayName("Mật khẩu:")]
        public string Password { get; set; }

        [Required(ErrorMessage = "Chưa nhập \"Xác nhận mật khẩu\"")]
        [DataType(DataType.Password)]
        [DisplayName("Xác nhận mật khẩu:")]
        public string ConfirmPassword { get; set; }

        [DisplayName("Thêm giỏ hàng?")]
        public bool MigrateToCart { get; set; }
    }

    public class LogInModel
    {
        [Required]
        [DisplayName("Tên đăng nhập")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Mật khẩu")]
        public string Password { get; set; }

        [DisplayName("Ghi nhớ?")]
        public bool RememberMe { get; set; }
    }

    #endregion

    // FormAuthentication and Membership Services
    #region Services

    // The FormsAuthentication type is sealed and contains static members, so it is difficult to
    // unit test code that calls its members. The interface and helper class below demonstrate
    // how to create an abstract wrapper around such a type in order to make the AccountController
    // code unit testable.

    public interface IMembershipService
    {
        int MinPasswordLength { get; }

        bool ValidateUser(string userName, string password);
        MembershipCreateStatus CreateUser(string userName, string password, string email);
        bool ChangePassword(string userName, string oldPassword, string newPassword);

        // Additions to Interface for EmailConfirmation, ...
        void SendConfirmationEmail(string userName);
        string SendResetPasswordEmail(string userName);
    }

    public class MembershipService : IMembershipService
    {
        private readonly MembershipProvider _provider;

        public MembershipService() : this(null)
        {
        }

        public MembershipService(MembershipProvider provider)
        {
            _provider = provider ?? Membership.Provider;
        }

        public int MinPasswordLength
        {
            get
            {
                return _provider.MinRequiredPasswordLength;
            }
        }

        public bool ValidateUser(string userName, string password)
        {
            if (String.IsNullOrEmpty(userName))
            {
                throw new ArgumentException("Value cannot be null or empty.", "userName");
            }
            if (String.IsNullOrEmpty(password))
            {
                throw new ArgumentException("Value cannot be null or empty.", "password");
            }
            return _provider.ValidateUser(userName, password);
        }

        public MembershipCreateStatus CreateUser(string userName, string password, string email)
        {
            if (String.IsNullOrEmpty(userName))
            {
                throw new ArgumentException("Value cannot be null or empty.", "userName");
            }
            if (String.IsNullOrEmpty(password))
            {
                throw new ArgumentException("Value cannot be null or empty.", "password");
            }
            if (String.IsNullOrEmpty(email))
            {
                throw new ArgumentException("Value cannot be null or empty.", "email");
            }
            MembershipCreateStatus status;
            // ORIGINAL: 6th Parameter is IsApproved property - which defaults to true
            // _provider.CreateUser(userName, password, email, null, null, true, null, out status);
            // MODIFICATION: Set the IsApproved property to false
            _provider.CreateUser(userName, password, email, null, null, false, null, out status);
            return status;
        }

        public bool ChangePassword(string userName, string oldPassword, string newPassword)
        {
            if (String.IsNullOrEmpty(userName))
            {
                throw new ArgumentException("Value cannot be null or empty.", "userName");
            }
            if (String.IsNullOrEmpty(oldPassword))
            {
                throw new ArgumentException("Value cannot be null or empty.", "oldPassword");
            }
            if (String.IsNullOrEmpty(newPassword))
            {
                throw new ArgumentException("Value cannot be null or empty.", "newPassword");
            }

            // The underlying ChangePassword() will throw an exception rather
            // than return false in certain failure scenarios.
            try
            {
                MembershipUser currentUser = _provider.GetUser(userName, true);
                return currentUser.ChangePassword(oldPassword, newPassword);
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (MembershipPasswordException)
            {
                return false;
            }
        }

        public void SendConfirmationEmail(string userName)
        {
            MembershipUser user = Membership.GetUser(userName);
            string confirmationGuid = user.ProviderUserKey.ToString();
            string host = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
            string verifyUrl = host + "/Account/RegisterEmail?ID=" + confirmationGuid;
            var message = new MailMessage("youming2007@gmail.com", user.Email)
            {
                Subject = "Xác nhận mật khẩu",
                Body = String.Format("Cám ơn quý khách đã đăng ký tài khoản ở trang web: {0}.\n\nXin quý khách vui lòng nhấn vào liên kết {1} để hoàn tất việc đăng ký!", host, verifyUrl)
            };
            var client = new SmtpClient();
            client.Send(message);
        }

        public string SendResetPasswordEmail(string userName)
        {
            MembershipUser user = Membership.GetUser(userName);
            string confirmationGuid = user.ProviderUserKey.ToString();
            string host = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
            string verifyUrl = host + "/Account/ResetPasswordEmail?ID=" + confirmationGuid;
            var message = new MailMessage("youming2007@gmail.com", user.Email)
            {
                Subject = "Khôi phục mật khẩu",
                Body = String.Format("Xin quý khách vui lòng nhấn vào liên kết {0} để hoàn tất việc khôi phục mật khẩu!", verifyUrl)
            };
            var client = new SmtpClient();
            client.Send(message);
            return user.Email;
        }
    }

    public interface IFormsAuthenticationService
    {
        void SignIn(string userName, bool createPersistentCookie);
        void SignOut();
    }

    public class FormsAuthenticationService : IFormsAuthenticationService
    {
        public void SignIn(string userName, bool createPersistentCookie)
        {
            if (String.IsNullOrEmpty(userName))
            {
                throw new ArgumentException("Value cannot be null or empty.", "userName");
            }
            FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
        }

        public void SignOut()
        {
            FormsAuthentication.SignOut();
        }
    }

    #endregion

    // PropertiesMustMatchAttribute and ValidatePasswordLengthAttribute
    #region Validation

    public static class AccountValidation
    {
        public static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://go.microsoft.com/fwlink/?LinkID=177550 for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                {
                    return "Tài khoản này đã có người sử dụng.";
                }
                case MembershipCreateStatus.DuplicateEmail:
                {
                    return "Email này đã được đăng ký.";
                }
                case MembershipCreateStatus.InvalidPassword:
                {
                    return "Mật khẩu không hợp lệ.";
                }
                case MembershipCreateStatus.InvalidEmail:
                {
                    return "Email không hợp lệ.";
                }
                case MembershipCreateStatus.InvalidAnswer:
                {
                    return "Câu trả lời không đúng.";
                }
                case MembershipCreateStatus.InvalidQuestion:
                {
                    return "Câu hỏi không hợp lệ.";
                }
                case MembershipCreateStatus.InvalidUserName:
                {
                    return "Tài khoản không hợp lệ.";
                }
                case MembershipCreateStatus.ProviderError:
                {
                    return "Lỗi Provider.";
                }
                case MembershipCreateStatus.UserRejected:
                {
                    return "Tài khoản không được khởi tạo.";
                }
                default:
                {
                    return "Lỗi không xác định.";
                }
            }
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public sealed class PropertiesMustMatchAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
        private readonly object _typeId = new object();

        public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty) : base(_defaultErrorMessage)
        {
            OriginalProperty = originalProperty;
            ConfirmProperty = confirmProperty;
        }

        public string ConfirmProperty { get; private set; }
        public string OriginalProperty { get; private set; }

        public override object TypeId
        {
            get
            {
                return _typeId;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, OriginalProperty, ConfirmProperty);
        }

        public override bool IsValid(object value)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
            object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
            object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
            return Object.Equals(originalValue, confirmValue);
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class ValidatePasswordLengthAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' must be at least {1} characters long.";
        private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength;

        public ValidatePasswordLengthAttribute() : base(_defaultErrorMessage)
        {
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, name, _minCharacters);
        }

        public override bool IsValid(object value)
        {
            string valueAsString = value as string;
            return (valueAsString != null && valueAsString.Length >= _minCharacters);
        }
    }

    #endregion
}